#include    "session.h"
#include    "storage.h"
#include    "response.h"
#include    "../utils/utils.h"
#include    "../utils/datetime.h"
#include    "../utils/logger.h"

#include    <microhttpd.h>

extern std::string lua_tojson(lua_State *, int);
static bool session_enabled = true;

struct SessionIdFinder {
    bool found;
    std::string id;
};

static int CookieIterator(void * p, MHD_ValueKind kind, const char * key, const char * val) {
    SessionIdFinder * finder = (SessionIdFinder *)p;

    if (std::string(key) == "session_id") {
        finder->found = true;
        finder->id = val;
    }

    return MHD_YES;
}

Session::Session(lua_State * lua, struct MHD_Connection * conn, class Response * rsp)
    : _vm(lua), _id() {
    if (!session_enabled) return;

    SessionIdFinder finder;
    finder.found = false;
    finder.id = "";

    MHD_get_connection_values(conn, MHD_COOKIE_KIND, &CookieIterator, &finder);

    if (!finder.found) {
        std::string id = CreateId();
        std::string cookie_set = "session_id=";
        cookie_set.append(id);
        cookie_set.append(";expires=");
        cookie_set.append(GMTime(time(NULL) + (time_t)(Storage::Instance().GetExpire() / 1000)));
        cookie_set.append(";path=/");
        rsp->Cookie(cookie_set);

        _id = id;
        lua_newtable(_vm);
        lua_setglobal(_vm, "session");
    } else {
        _id = finder.id;

        std::string data;
        if (Storage::Instance().TryGet(_id, data)) {
            int top = lua_gettop(_vm);

            lua_getglobal(_vm, "debug");
            lua_getfield(_vm, -1, "traceback");
            lua_remove(_vm, -2);

            lua_getglobal(_vm, "json");
            lua_getfield(_vm, -1, "decode");
            lua_remove(_vm, -2);
            lua_pushlstring(_vm, data.c_str(), data.size());

            if (0 != lua_pcall(_vm, 1, 1, top + 1)) {
                Logger::Instance().Error("Failed to start session. %s", lua_tostring(_vm, -1));
                lua_newtable(_vm);
            }

            lua_setglobal(_vm, "session");
            lua_settop(_vm, top);
        } else {
            lua_newtable(_vm);
            lua_setglobal(_vm, "session");
        }
    }
}

Session::~Session() {
    if (!session_enabled) return;
    int top = lua_gettop(_vm);
    lua_getglobal(_vm, "session");
    std::string json = lua_tojson(_vm, -1);
    Storage::Instance().Set(_id, json);
    lua_settop(_vm, top);
}

void Session::Init(bool enabled) {
    session_enabled = enabled;
}
